#    Copyright 2012 New Mexico EPSCoR
#
#    This file is part of Teacher's P.E.T.
#
#    Teacher's P.E.T. is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    Teacher's P.E.T. is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with Teacher's P.E.T..  If not, see <http://www.gnu.org/licenses/>.
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.http import Http404, HttpResponseRedirect, HttpResponse
from django.contrib import messages
from django.utils.encoding import smart_str
from django.views.decorators.cache import never_cache

from filetransfers.api import serve_file
from options import Options
from apps.management import Management
from apps.course.models import Course
from apps.course.functions import course_reverse, remove_course
from apps.participants.models import CourseParticipant
from apps.gradebook.models import GradingSectionGradeForm
from apps.contact.views import contact_list
from models import *
from functions import *

import logging
import urllib

logger = logging.getLogger('pet.apps.activities.manage')

def GetTemplateContent(Template):
    return "{%%comment%%} From the %s Template {%%endcomment%%} %s {%%comment%%} End of %s Template {%%endcomment%%}" % (Template.title,Template.content,Template.title)

def make_activity_list(list, level=0):
    Result = []
    for activity in list:
        activity.indent = "&nbsp;&nbsp;&nbsp;"*level
        Result.append(activity)
        SubactivityObjects = activity.get_subactivities()
        if SubactivityObjects:
            Result.extend(make_activity_list(SubactivityObjects,level+1))
    return Result
            
@Options.option_enabled
@never_cache
def manage(request, TheCourseParticipant, TheCourse):
    logger.fxn_entry('Entering activity management')
    #Grab all the activities
    AllActivities = Activity.find(course=TheCourse.course_id)

    #Go through each activity and remove the ones who have parents
    Activities = []
    ActivityList = []
    DownloadAllEnabled = False
    if AllActivities:
        for activity in AllActivities:
            if not activity.get_parent_activities():
                ActivityList.append(activity)
            if activity.questions.all():
                DownloadAllEnabled = True
        #Now add in the rest of the activities based on parentage
        Activities = make_activity_list(ActivityList)

    #Grab the templates
    Templates = ActivityTemplate.find(keep_queryset=True)
    
    logger.fxn_exit("Returning activity management")
    return render_to_response('activities/manage.html',{'Activities':Activities, 'Templates':Templates, 'DownloadAllEnabled':DownloadAllEnabled},context_instance=RequestContext(request))
Management.register_interface('Activities','ASSISTANT;INSTRUCTOR','manage',manage)

@Options.option_enabled
def remove(request, TheCourseParticipant, TheCourse, category=None, title=None):
    logger.fxn_entry('category %s, title %s', category, title)
    TheActivity = Activity.find_or_404(course=TheCourse.course_id, title_slug=title, category_slug=category)
    if request.method == 'POST':
        Result = remove_course(TheActivity, TheCourse.course_id)
        if (Result != "None"):
            messages.info(request,"Activity %s %s" % (TheActivity, Result))
        logger.fxn_exit('Returning result %s',Result)
        return HttpResponseRedirect(course_reverse('management',TheCourse,app='activities', command='manage'))
    
    logger.fxn_exit("Showing activity delete confirmation")
    return render_to_response('activities/delete.html',{'TheActivity':TheActivity},context_instance=RequestContext(request))
Management.register_interface('Activities','ASSISTANT;INSTRUCTOR','remove',remove)

@Options.option_enabled
def remove_template(request, TheCourseParticipant, TheCourse, id=None):
    logger.fxn_entry('id %s', id)
    TheTemplate = ActivityTemplate.find_or_404(id=id)
    if request.method == 'POST':
        TheTemplate.delete()
        messages.info(request,"Template '%s' has been deleted" % (TheTemplate))
        logger.fxn_exit('Template %s deleted',TheTemplate)
        return HttpResponseRedirect(course_reverse('management',TheCourse,app='activities', command='manage'))
    
    logger.fxn_exit("Showing template delete confirmation")
    return render_to_response('activities/delete_template.html',{'TheTemplate':TheTemplate},context_instance=RequestContext(request))
Management.register_interface('Activities','ASSISTANT;INSTRUCTOR','remove_template',remove_template)

@Options.option_enabled
def copy_template(request, TheCourseParticipant, TheCourse, id):
    logger.fxn_entry('id %s',id)
    TheTemplate = ActivityTemplate.find_or_404(id=id)
    TheNewTemplate = ActivityTemplate(category=TheTemplate.category, title=TheTemplate.title + " - Copy", description=TheTemplate.description, example=TheTemplate.example, content=TheTemplate.content)
    TheNewTemplate.save()
    logger.fxn_exit("Created new template as copy of template %s. Returning edit_template for new copy" % id)
    return HttpResponseRedirect(course_reverse('management',TheCourse,app='activities', command='edit_template', arguments="%s" % (TheNewTemplate.id)))
Management.register_interface('Activities','ASSISTANT;INSTRUCTOR','copy_template',copy_template)
    
@Options.option_enabled
def edit_template(request, TheCourseParticipant, TheCourse, id=None):
    logger.fxn_entry('id %s',id)
    TheTemplate = None
    if id: TheTemplate = ActivityTemplate.find(id=id)
    
    if request.method == "POST":
        TemplateCreated = False
        TemplateChanged = False
        Error = False
        TemplateForm = ActivityTemplateForm(request.POST, instance=TheTemplate)
        if TemplateForm.is_valid():
            if TemplateForm.has_changed():
                TemplateChanged = True
                if not TheTemplate: TemplateCreated = True
                TheTemplate = TemplateForm.save()
            else: logger.form_unchanged("TemplateForm unchanged")
        else: Error = True; logger.debug("TemplateForm %s invalid",TemplateForm)
        
        if not Error:
            if 'Save' in request.POST and request.POST['Save'] == 'SaveReturn':
                logger.fxn_exit('Redirecting to activities management')
                return HttpResponseRedirect(course_reverse('management',TheCourse,app='activities'))
        
            if TemplateCreated and 'Save' in request.POST and request.POST['Save'] == 'SaveEdit':
                logger.fxn_exit('Redirecting to self with newly created Template')
                return HttpResponseRedirect(course_reverse('management',TheCourse,app='activities', command='edit_template', arguments="%s" % (TheTemplate.id)))
    else:
        TemplateForm = ActivityTemplateForm(instance=TheTemplate)
    
    logger.fxn_exit('Returning edit template form')
    return render_to_response('activities/edit_template.html',{'TemplateForm':TemplateForm,'TheTemplate':TheTemplate},context_instance=RequestContext(request))
Management.register_interface('Activities','ASSISTANT;INSTRUCTOR','edit_template',edit_template)

@Options.option_enabled
def edit(request, TheCourseParticipant, TheCourse, category=None, title=None, template=None):
    logger.fxn_entry('category %s, title %s, template %s', category, title, template)
    TheActivity = None
    TheTemplate = None
    if category or title: TheActivity = Activity.find(course=TheCourse.course_id, title_slug=title, category_slug=category)
    if template: TheTemplate = ActivityTemplate.find(template)
    
    Anchor = ""
    Error = False
    if request.method == "POST":
        #Save the new post
        ActivityForm, QuestionForms = CreateActivityFormset(TheActivity, request.POST,course=TheCourse.course_id)
        ActivityChanged = False
        ActivityCreated = False
        QuestionsChanged = False
        #Save whatever was entered
        if ActivityForm.is_valid():
            if ActivityForm.has_changed():
                ActivityChanged = True
                if not TheActivity: ActivityCreated = True
                ActivityForm.instance.course = TheCourse.course_id
                TheActivity = ActivityForm.save()
            else: logger.form_unchanged("ActivityForm unchanged")
        else: Error = True; logger.debug("ActivityForm %s invalid",ActivityForm)
        
        #Save responses first since question numbers may change
        for questionform,responses in QuestionForms:
            for response in responses:
                if response.is_valid():
                    if response.has_changed():
                        response.save()
                    else: logger.form_unchanged("Response unchanged")
                else: Error = True; logger.debug("Response invalid")
        
        #Check for template appending
        if 'Template' in request.POST and TheActivity:
            TheTemplate = ActivityTemplate.find(id=request.POST['Template'])
            if TheTemplate:
                TheActivity.content += GetTemplateContent(TheTemplate)
                TheActivity.save()
                Anchor = "activitycontent"
                
        #Check for new response
        if 'AddResponse' in request.POST and TheActivity:
            TheQuestion = TheActivity.questions.filter(section_number=request.POST['AddResponse'])
            if TheQuestion:
                ActivityChanged = True
                Anchor = "section%s" % request.POST['AddResponse']
                NewResponse = ActivityAutogradeResponse(question=TheQuestion[0])
                NewResponse.save()
        
        if 'DeleteResponse' in request.POST and TheActivity:
            parameters = request.POST['DeleteResponse'].split(';')
            TheQuestion = TheActivity.questions.filter(section_number=parameters[0])
            if TheQuestion:
                ActivityChanged = True
                Anchor = "section%s" % parameters[0]
                TheResponse = ActivityAutogradeResponse.objects.filter(question=TheQuestion[0],id=parameters[1])
                if TheResponse:
                    TheResponse[0].delete()
                    
        #Save questions
        for questionform,responses in QuestionForms:
            if questionform.is_valid():
                if questionform.has_changed() and TheActivity:
                    questionform.instance.content_object = TheActivity
                    QuestionsChanged = True
                    questionform.save()
                else: logger.form_unchanged("QuestionForm unchanged or activity %s doesn't exist",TheActivity)
            else: Error = True; logger.debug("QuestionForm %s invalid",questionform)
        
        if 'DeleteSection' in request.POST:
            if TheActivity:
                ActivityChanged = True
                TheSection = TheActivity.sections.filter(section_number=request.POST['DeleteSection'])
                if TheSection:
                    TheSection[0].delete()
                    
        #Reorder questions since questions have changed
        if QuestionsChanged:
            index = 1
            Sections = TheActivity.sections.order_by('section_number')
            for question in Sections:
                question.section_number = index
                question.save()
                index += 1
                
        #Now check for changes/additions
        if 'NewQuestion' in request.POST:
            if TheActivity:
                logger.debug("Adding new question")
                ActivityChanged = True
                NewQuestion = ActivityQuestion(content_object=TheActivity,start_date=TheActivity.start_date,end_date=TheActivity.end_date)
                NewQuestion.save()
                Anchor = "section%s" % NewQuestion.section_number
            else:
                logger.debug("No new question section - Activity is %s ",TheActivity)
        
        if 'NewQuestion5' in request.POST:
            if TheActivity:
                logger.info("Adding 5 new questions")
                ActivityChanged = True
                NewQuestion = ActivityQuestion(content_object=TheActivity,start_date=TheActivity.start_date,end_date=TheActivity.end_date)
                NewQuestion.save()
                NewQuestion = ActivityQuestion(content_object=TheActivity,start_date=TheActivity.start_date,end_date=TheActivity.end_date)
                NewQuestion.save()
                NewQuestion = ActivityQuestion(content_object=TheActivity,start_date=TheActivity.start_date,end_date=TheActivity.end_date)
                NewQuestion.save()
                NewQuestion = ActivityQuestion(content_object=TheActivity,start_date=TheActivity.start_date,end_date=TheActivity.end_date)
                NewQuestion.save()
                NewQuestion = ActivityQuestion(content_object=TheActivity,start_date=TheActivity.start_date,end_date=TheActivity.end_date)
                NewQuestion.save()
                Anchor = "section%s" % NewQuestion.section_number
            else:
                logger.debug("No new 5 question section - Activity is %s ",TheActivity)
                    
        if not Error:
            if 'Save' in request.POST and request.POST['Save'] == 'SaveReturn':
                logger.fxn_exit('Returning to activity management')
                return HttpResponseRedirect(course_reverse('management',TheCourse.course_id,app='activities'))
            if 'Save' in request.POST and request.POST['Save'] == 'SaveGrade':
                logger.fxn_exit('Redirecting to gradebook for this activity')
                return HttpResponseRedirect(course_reverse('management',TheCourse.course_id,app='activities',comand='manage',arguments="%s&&%s"%(TheActivity.category_slug,TheActivity.title_slug)))
            if ActivityCreated:
                logger.fxn_exit('Redirecting to self to load newly created activity')
                return HttpResponseRedirect(course_reverse('management',TheCourse.course_id,app='activities',command='edit',arguments="%s&%s"%(TheActivity.category_slug,TheActivity.title_slug)))
            if 'Save' in request.POST and request.POST['Save'] == 'SaveEdit':
                logger.fxn_exit('Redirecting to self to continue editing')
                return HttpResponseRedirect(course_reverse('management',TheCourse.course_id,app='activities', command='edit', arguments="%s&%s" % (TheActivity.category_slug,TheActivity.title_slug)))
            if TheActivity and (ActivityChanged or QuestionsChanged):
                #Recreate the formset to include anything that may have been added/removed
                ActivityForm, QuestionForms = CreateActivityFormset(TheActivity)
        
            
        
    elif TheActivity:
        ActivityForm, QuestionForms = CreateActivityFormset(TheActivity,course=TheCourse.course_id)
    else:
        #Create a dummy activity so that we can have the course set in the new form
        ActivityForm, QuestionForms = CreateActivityFormset(Activity(course=TheCourse.course_id,content=GetTemplateContent(TheTemplate) if TheTemplate else ''),course=TheCourse.course_id)
    
    if Anchor and not Error:
        arguments = ""
        if category and title: arguments = "%s&%s" % (TheActivity.category_slug, TheActivity.title_slug)
        elif category: arguments = TheActivity.category_slug
        logger.fxn_exit('Returning a redirect to self with appended anchor')
        return HttpResponseRedirect(course_reverse('management',TheCourse.course_id,app='activities', command='edit', arguments=arguments) + '#' + Anchor)
    
    Templates = ActivityTemplate.find(keep_queryset=True)
    
    logger.fxn_exit('Returning edit form')
    return render_to_response('activities/edit_activity.html',{'ActivityForm':ActivityForm,'TheActivity':TheActivity,'QuestionForms':QuestionForms,'Templates':Templates},context_instance=RequestContext(request))
Management.register_interface('Activities','ASSISTANT;INSTRUCTOR','edit',edit)

def results(request, TheCourseParticipant, TheCourse, category, title, question=None):
    logger.fxn_entry('category %s, title %s, question %s', category, title, question)
    TheActivity = Activity.find_or_404(course=TheCourse.course_id,title_slug=title,category_slug=category)
    Metrics = GenerateMetrics(activity=TheActivity,course=TheCourse,question=question)
    
    logger.fxn_exit('Returning results')
    return render_to_response('activities/results.html',{'Activity':TheActivity,'Metrics':Metrics},context_instance=RequestContext(request))
Management.register_interface('Activities','ASSISTANT;INSTRUCTOR','results',results)

def remind(request, TheCourseParticipant, TheCourse, category, title):
    logger.fxn_entry('category %s, title %s', category, title)
    TheActivity = Activity.find_or_404(course=TheCourse.course_id,title_slug=title,category_slug=category)
    completed_dict = TheActivity.get_completed_info()
    initial_subject = "Reminder: You still need to complete %s" % TheActivity.title
    initial_message = ("You are receiving this reminder because you "
                       "have not yet completed %s. Please complete it "
                       "as soon as possible." % TheActivity.title)
    Response = contact_list(request, TheCourse, [user.email for user in completed_dict['notcompleted']], "Students who haven't completed %s" % TheActivity.title, initial_subject=initial_subject, initial_message=initial_message)
    Response['Content-type'] = "no-management:%s" % Response['Content-type']
    logger.fxn_exit('Returning reminder response')
    return Response
Management.register_interface('Activities','ASSISTANT;INSTRUCTOR','remind',remind)

def add_text_to_zip(response_dict, zip_file, zip_dir):
    for question, responses in response_dict.items():
        if question.type == "UPLOAD": continue
        for response in responses:
            if not response.response: logger.debug("No response from %s", response.user); continue
            logger.debug("Have response from %s",response.user, question.type)
            zip_file.writestr(smart_str("%s/%s.%s.%d.txt" % (zip_dir,response.user.last_name,response.user.first_name, response.section.section_number)), smart_str("Question: %s\r\nResponse: %s" % (question.question,response.get_response(question))))
    return zip_file

def add_file_to_zip(request, response_dict, zip_file, zip_dir):
    for question, responses in response_dict.items():
        if question.type != "UPLOAD": continue
        for response in responses:
            if not response.response: continue
            result = serve_file(request, response.response)
            filename = '.'.join(os.path.basename(response.response.name).split('.')[2:])
            zip_file.writestr(smart_str("%s/%s.%s.%d.%s" % (zip_dir, response.user.last_name, response.user.first_name, response.section.section_number, filename)), smart_str(result.content))
    return zip_file
    
def download(request, TheCourseParticipant, TheCourse, category, title):
    logger.fxn_entry('category %s, title %s',category,title)
    TheActivity = Activity.find_or_404(TheCourse.course_id, category_slug=category, title_slug=title)
    ActivityResponses = CreateActivityResponseSet(TheActivity)
    if not ActivityResponses:
        messages.info(request, "No responses were found for this activity")
        logger.forced_redirect('No responses were found for this activity')
        return HttpResponseRedirect(course_reverse('management',TheCourse.course_id,app='activities'))
    
    #Pack the responses together in a zipfile
    zipstream = StringIO.StringIO()
    directory_in_zip = "%s/activities/%s/%s" % (TheCourse.course_id, TheActivity.category_slug, TheActivity.title_slug)
    try:
        zip_file = zipfile.ZipFile(zipstream,"w",zipfile.ZIP_DEFLATED)
    except RuntimeError:
        zip_file = zipfile.ZipFile(zipstream,"w",zipfile.ZIP_STORED)
    add_text_to_zip(ActivityResponses,zip_file, directory_in_zip)
    add_file_to_zip(request,ActivityResponses,zip_file, directory_in_zip)
    zip_file.close()
    zipstream.seek(0,os.SEEK_END)
    zip_size = zipstream.tell()
    zipstream.seek(0)
        
    #Serve the zip file
    response = HttpResponse(zipstream.getvalue(), content_type='application/x-zip-compressed')
    response['Content-Disposition'] = 'attachment; filename=%s.%s.%s.zip' % (TheCourse.course_id, TheActivity.category_slug, TheActivity.title_slug)
    response['Content-Length'] = zip_size
    
    logger.fxn_exit('Returning file')
    return response
Management.register_interface('Activities','ASSISTANT;INSTRUCTOR','download',download)

def download_all(request, TheCourseParticipant, TheCourse):
    logger.fxn_entry('')
    TheActivities = Activity.find_or_404(TheCourse.course_id)
    zipstream = StringIO.StringIO()
    try:
        zip_file = zipfile.ZipFile(zipstream,"w",zipfile.ZIP_DEFLATED)
    except RuntimeError:
        zip_file = zipfile.ZipFile(zipstream,"w",zipfile.ZIP_STORED)
    for TheActivity in TheActivities:
        ActivityResponses = CreateActivityResponseSet(TheActivity)
        if not ActivityResponses: continue
        
        #Pack the responses together in a zipfile
        directory_in_zip = "%s/activities/%s/%s" % (TheCourse.course_id, TheActivity.category_slug, TheActivity.title_slug)
        add_text_to_zip(ActivityResponses,zip_file, directory_in_zip)
        add_file_to_zip(request,ActivityResponses,zip_file, directory_in_zip)
    zip_file.close()
    zipstream.seek(0,os.SEEK_END)
    zip_size = zipstream.tell()
    zipstream.seek(0)
        
    #Serve the zip file
    response = HttpResponse(zipstream.getvalue(), content_type='application/x-zip-compressed')
    response['Content-Disposition'] = 'attachment; filename=%s.all_activitys.zip' % (TheCourse.course_id)
    response['Content-Length'] = zip_size
    
    logger.fxn_exit('Returning files')
    return response
Management.register_interface('Activities','ASSISTANT;INSTRUCTOR','download_all',download_all)

